﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using KConst;

namespace Twin
{
    class KMsg8
    {
        public const int LEN_HEAD8 = 8;
        public const byte VER_MARK = (byte)'J';    //KUI-S协议
    
        //KUI-S协议: 
        public const int OFFSET_VER         = 0;    //Length : 1
        public const int OFFSET_LEN         = 1;    //Length : 1
        public const int OFFSET_ID          = 2;    //Length : 1
        public const int OFFSET_OP          = 3;    //Length : 1
        public const int OFFSET_PROPERTY    = 4;    //Length : 1
        public const int OFFSET_INDEX       = 5;    //Length : 1
        public const int OFFSET_RESERVED    = 6;    //Length : 1
        public const int OFFSET_CRC         = 7;   //Length : 1
    
        public const int HEAD_OK = 0;
        public const int HEAD_ERR_LENGTH_NOT_ENOUGH = 1;
        public const int HEAD_ERR_VER_MARK = 2;
        public const int HEAD_ERR_CRC = 3;
        public const int HEAD_A_R = 4;
    
    
        private byte[] head = new byte[KMsg8.LEN_HEAD8];
        private byte[] body;
        private byte[] msg;
    
        private class strBody{
            //String index;
            //String total;
            //String seg;
        }
    
        //Make msg[], head[], body[] ready 
        public bool load(byte[] msg0)
        {
            int len = msg0.Length;
            if(len < KMsg8.LEN_HEAD8){        //head长度不足
                return false; }
        
            msg = new byte[len];
            Buffer.BlockCopy(msg0, 0, msg, 0, len);     //msg ready
            Buffer.BlockCopy(msg0, 0, head, 0, KMsg8.LEN_HEAD8);
            if(isHeadValid(head) != HEAD_OK){
                return false;}                  //head ready !
        
            int lenPack = getMsgLen() - KMsg8.LEN_HEAD8;
            if(lenPack < 0){return false;}
            if(lenPack == 0){return true;}      //纯HEAD
        
            body = new byte[lenPack];
            //try{
                Buffer.BlockCopy(msg0, KMsg8.LEN_HEAD8, body, 0, lenPack);     //body ready !
            //}
            //catch(Exception e){
                //TextOut.println(KGet.HHmmSS() + " " + this.toString() + " " + e.getMessage());;
            //    return false;
            //}
        
            if(isBodyValid(body, lenPack) == false){
                return false;}
            return true;
        }
    
    
        public int make(){
            //head len
            int msglen = head.Length;
            if(body != null){msglen += body.Length; }
            msg = new byte[msglen];
        
            //Head
            head[OFFSET_LEN] = StrBytes.LoOfShort((short)(msglen - 1));
            head[OFFSET_CRC] = Crc.GetCRC8(head, 0, KMsg8.LEN_HEAD8 - 1);       //head crc
            Buffer.BlockCopy(head, 0, msg, 0, head.Length);
        
            //Body
            if(msglen > KMsg8.LEN_HEAD8){     //长Msg
                //body crc
                int lenBody = body.Length;
                int body_crc = Crc.GetCRC(body, KConst.BODY0, lenBody - 2);
                int offset_crc_l = body.Length - 2;
                body[offset_crc_l] = StrBytes.LoOfShort((short)body_crc);
                body[offset_crc_l + 1] = StrBytes.HiOfShort((short)body_crc);

                Buffer.BlockCopy(body, 0, msg, KMsg8.LEN_HEAD8, body.Length);
            }

            return msglen;
        }
    
        //不存在bBody/sBody时，用null
        public void setBody(byte[] bBody, String sBody){   
            int bBodyLen = 0;
            int sBodyLen = 0;
        
            if(bBody != null){bBodyLen = bBody.Length;}
            if(sBody != null){sBodyLen = sBody.Length;}
        
            if((sBody == null)&&(bBodyLen == 0)){
                body = null;
                return;
            }
        
            int lenBody = 0;
            if(bBody != null){lenBody = 1 + bBodyLen;}
            lenBody += sBodyLen + KConst.WIDTH_CRC16;
            body = new byte[lenBody];
        
            //bBody => msg
            int offset = 0;
            if(bBody != null){
                short lenB = (short)bBodyLen;

                body[0] = StrBytes.LoOfShort(lenB);
                //body[1] = StrBytes.HiOfShort(lenB);
                if(lenB > 0){
                    Buffer.BlockCopy(bBody, 0, body, 1, lenB);
                }
                offset += lenB + 1;
            }

            //sBody => msg
            if(sBody != null){
                byte[] bStr = Encoding.ASCII.GetBytes(sBody);   // sBody.getBytes();
                int lenS = sBody.Length;
                Buffer.BlockCopy(bStr, 0, body, offset, lenS);
            }
        }
    
        public static int isHeadValid(byte[] buf){
            if(buf.Length == 1){
                return HEAD_A_R;
            }
        
            if(buf.Length < LEN_HEAD8){
                return HEAD_ERR_LENGTH_NOT_ENOUGH;}
        
            if(buf[OFFSET_VER] != VER_MARK){
                return HEAD_ERR_VER_MARK;}
        
            bool r = Crc.CheckCRC8(buf, 0, LEN_HEAD8 - 1);
            if(r == false){return HEAD_ERR_CRC;}
            return HEAD_OK;
        }
    
        public static bool isBodyValid(byte[] body, int len){
            return Crc.CheckCRC8(body, 0, len - 2);
        }
    
        //======================== get =============================================
        public byte[] getHead(){
            return head;
        }
        public byte[] getMsg(){
            return msg;
        }
        //======================== get =============================================
    
        //msg总长度
        public int getMsgLen(){
            int i = KGet.unsignedByte(head[OFFSET_LEN]);     //.Short(head, KConst.OFFSET_LEN);
            return ++i;
        }
    
        //m.load first!
        public String getBodyStr(){
            bool fWITH_STR = getBitPP(KConst.PP.bitWITH_STR, head[OFFSET_PROPERTY]);
            if(fWITH_STR == false){return null;}

            bool fWITH_BIN = getBitPP(KConst.PP.bitWITH_BIN, head[OFFSET_PROPERTY]);
            int lenBin = 0;
            if(fWITH_BIN == true){
               lenBin = body[0] + 1;        //2;    
            }
    
            int lenStr = getMsgLen() - LEN_HEAD8 - lenBin - KConst.WIDTH_CRC16;       //CRC
            byte[] b = new byte[lenStr];     
        
            Buffer.BlockCopy(body, lenBin, b, 0, lenStr);
            //b[lenStr] = '\0';

            String s = Encoding.ASCII.GetString(b); 
            /*
            String s = null;
            try{
                s = new String(b,"US-ASCII");
                s = s.trim();
            }catch(Exception e){
                //TextOut.println(KGet.HHmmSS() + " " + this.toString() + " " + e.getMessage());;
            }*/
            return s;
        }
    
        //m.load first!
        public byte[] getBodyBin(){
            short len = StrBytes.getShort(body, 0);
            byte[] b = new byte[len];     
            //try{
                Buffer.BlockCopy(body, 1, b, 0, len);
            //}catch(Exception e){
                //TextOut.println(KGet.HHmmSS() + " " + this.toString() + " " + e.getMessage());;
            //}
        
            return b;
        }
    
        public KMsg8(){
            head[OFFSET_VER] = VER_MARK;
        }

        //======= set ==============================================================
        public void setVer(byte ver){
            head[OFFSET_VER] = ver;
        }
    
        public void setLen(int len){
            --len;
            head[OFFSET_LEN] = (byte)(len & 0xff);
            //head[KConst.OFFSET_LEN + 1] = (byte)((len >> 8) & 0xff);
        }
    
        public void setOp(byte OpType){
            head[OFFSET_OP] = OpType;
        }
    
        public void setId(String sId){
            int Id;
            //try{
                Id = int.Parse(sId);   // Integer.valueOf(sId);
            //}catch(Exception e){
                //TextOut.println(KGet.HHmmSS() + " " + Sys.getLineNoOfFile() + ", " + e.getMessage());
            //    return;
            //}
        
            head[OFFSET_ID] = (byte)(Id & 0xff);
            //head[KConst.OFFSET_ID + 1] = (byte)((Id>>8) & 0xff);
            //head[KConst.OFFSET_ID + 2] = (byte)((Id>>16) & 0xff);
            //head[KConst.OFFSET_ID + 3] = (byte)((Id>>24) & 0xff);
        } 
    
        private void setProperty(short Property){
            head[OFFSET_PROPERTY] = (byte)(StrBytes.LoOfShort(Property));
            //head[KConst.OFFSET_PROPERTY + 1] = (byte)(StrBytes.HiOfShort(Property));
        }
    
        //for term index-stamping, e.g: 21-encode
        public void setIndex(byte Index){
            head[OFFSET_INDEX] = Index;
        }
    
    
        //======= get ==============================================================
        public byte getOp(){
            return head[OFFSET_OP];
        }
    
        public String getId(){
            int iId = KGet.Int(head, OFFSET_ID);
            /*int StaId = head[KConst.OFFSET_ID];
            StaId += head[KConst.OFFSET_ID + 1]<<8;
            StaId += head[KConst.OFFSET_ID + 2]<<16;
            StaId += head[KConst.OFFSET_ID + 3]<<24; */
            return Convert.ToString(iId);  // String.valueOf(iId);
        } 
    
        public short getProperty(){
            short Property = KGet.Short(head, OFFSET_PROPERTY);
            //short Property = head[KConst.OFFSET_PROPERTY];
            //Property += ((short)head[KConst.OFFSET_PROPERTY + 1])<<8;
            return Property; 
        }
    
        public byte getIndex(){
            //int Index = KGet.Int(head, KConst.OFFSET_INDEX);
            //return Index;
            return head[OFFSET_INDEX];
        }
    
        //12个数字：dddddd-dddddd
        /*public String getPSW(){
            String s = "";
            for(int i = 0; i<6; i++){
                s += String.valueOf(head[KConst.OFFSET_PSW + i]>>4);
                s += String.valueOf(head[KConst.OFFSET_PSW + i] & 0x0f);
            }
        
            StrBytes.Bytes2Hex(msg, 0)
            return s;
        }*/
    
        /*
        public String getPSW(){
            byte[] b = new byte[6];
            for(int i = 0; i<6; i++){
                b[i] = head[KConst.OFFSET_PSW + i];
            }
    
            String s = StrBytes.Bytes2Hex(b, 6);
            return s;
        }*/
        //==========================================================================
        /*public bool getAckDir(short PP){
            bool f = getBitPP(KConst.PP.bitINST_DIR, PP);
            return f;
        }*/
    
        public bool getBitDIR(){
            return getBitPP(KConst.PP.bitINST_DIR, getProperty());
        }
    
    
        public bool getBitPP(int bit, short PP){
            short tmp = PP;
            if(bit != 0){
                tmp = (short) (PP>>((byte)bit));
            }
            tmp &=0x0001;
            if(tmp == 0x0001){
                return true;}
            else{
                return false;}
        }
    
        private short setBitSHORT(int bit, short PP){
            short tmp = 0x0001;
            if(bit > 0){
                tmp = (short)(tmp<<(byte)bit);
            }
            tmp |= PP;
            return tmp;
        }
    
        private short clrBitSHORT(int bit, short PP){
            short tmp = 0x0001;
            if(bit > 0){
                tmp = (short)(tmp<<(byte)bit);
            }
            tmp = ((short) ~tmp);
            tmp &= PP;
            return tmp;
        }

    
        public void setBitPP(int bit_index, bool withBool){
            short pp = this.getProperty();
            if(withBool == true){
                pp = setBitSHORT(bit_index, pp);
            }
            else{
                pp = clrBitSHORT(bit_index, pp);
            }
            this.setProperty(pp);
        }
    
        public bool getBitCAT(){
            return getBitPP(KConst.PP.bitCAT, getProperty());
        }
    
        public bool getBitDest(){
            return getBitPP(KConst.PP.bitWITH_DEST, getProperty());
        }
    
        //==========================================================================
    
        //（例如: 
        // Areas: "dst=1001,1002^ADD|..."）
        // sKey: "dst="
        // Return: "1001,1002"
        public String pickValue(String sBody, String sKey){
            int pos = sBody.IndexOf(sKey);
            if(pos < 0){return "";}
            //
            sKey = sKey.Trim();
            pos += sKey.Length;
            int e = sBody.IndexOf("^", pos);  //"^"区号
        
            String s;
            if(e >= 0){
                s = sBody.Substring(pos, e);} //有区号结尾
            else{
                s = sBody.Substring(pos); }//无区号
        
            return s.Trim();   //"1001,1002"
        }
    
        //（例如: dst=1001,1002^）
        // sKey: "dst="
        public String getBodyPara(String sKey){
            String sBody = getBodyStr();
            String s = pickValue(sBody, sKey);
            if(s.Equals("")){
                //TextOut.println(KGet.HHmmSS() + " [" + sKey + "] not in " + sBody);
            }
            return s;
        }
    
    
        public static String Frame2Hex(byte[] frame){
            int len = KGet.unsignedByte(frame[OFFSET_LEN]) + 1;     //.Short(head, KConst.OFFSET_LEN);
            String s = StrBytes.Bytes2Hex(frame, len);
            return s;
        }
    
    
        //从sBody中移除第1个包含key的区Area:
        //例如：k = "dst=1001,1026^ADD|SETTINGS|..."
        /*
        public static String RemoveArea(String sBody, String key){
            String[] s = sBody.split("\\^");
            for(int i = 0; i< s[i].Length; i++){
                int pos = s[i].IndexOf(key);
                if(pos >= 0){    //存在
                    String ss = s[i].Substring(pos);
                    String sss = sBody.replaceFirst(ss + "\\^", "");   
                    return sss;
                }
            }
            return null; 
        }*/
    
    }
}
